(function (echarts) {
    var dataResults = [];
    var chartIndex = 0;
    var axisOptions = "";

    if (!echarts) {
        echarts = window.echarts;
    }

    function updateChartsOptions(chartsContainerElement) {
        axisOptions = "";
        $.each(dataResults[0], function (name) {
            if (name) {
                axisOptions += '<option val="' + name + '">' + name + '</option>';
            }
        });
        if (!chartsContainerElement) {
            chartsContainerElement = $("#chartsPanel");
        }
        //update all elements on page
        $(".my-x-axis", chartsContainerElement).empty();
        $(".my-y-series tbody", chartsContainerElement).empty();
        $(".my-x-axis", chartsContainerElement).append(axisOptions);
        $('.my-filter-expression', chartsContainerElement).val("");
        $(".my-group-expression", chartsContainerElement).val("");
        addNewSerieRowInTbody($(".my-y-series tbody", chartsContainerElement));
        $(".my-selected-y-serie", chartsContainerElement).append(axisOptions);
    }

    function onFileDataLoaded(data, columns) {
        dataResults = data;
        $("#dataGridContainer")
            .append("<table id='dataGrid' class='table table-striped table-bordered' width='100%'></table>");
        $('#dataGrid').DataTable({
            data: data,
            columns: columns,
            scrollX: true,
            destroy: true
        });
        updateChartsOptions();
    }

    function onParseFileComplete(results) {
        $("#loadingLabel").text("Succeed to load file");
        $("#loadingLabel").hide(3000);
        if (results && results.errors) {
            if (results.errors) {
                var errorCount = results.errors.length;
                var firstError = results.errors[0];
                console.error("Got " + errorCount + " errors to parse data file. " + firstError, + results.errors);
            }
            if (results.data && results.data.length > 0) {
                var rowCount = results.data.length;
                console.log("Got " + rowCount + " records from data file.");
            }
        }
        //console.log("parse file succeed Results:", results);
        if (results && results.data && results.data.length > 0) {
            dataResults = results.data;
            var columns = [];
            if ($('#header').prop('checked')) {
                $.each(results.data[0], function (name) {
                    columns.push({ data: name, title: name });
                });
            } else {
                $.each(results.data[0], function (index) {
                    columns.push({ title: "col-" + index });
                });
            }
            onFileDataLoaded(results.data, columns);
        }
    }

    function onParseFileError(err, file) {
        $("#loadingLabel").text("Failed to load file");
        $("#loadingLabel").hide(3000);
        console.error("Got error to read file '" + file + "'", err, file);
    }

    function handleFileSelect(evt) {
        var dataTable = $('#dataGrid').DataTable();
        if (dataTable) {
            dataTable.destroy();
        }
        $('#dataGridContainer').empty();
        var files = evt.target.files;
        //console.log(files);
        if (files.length > 0) {
            if (files[0].name.indexOf(".json") > 0) {
                var fileReader = new FileReader();
                fileReader.onerror = function (e) {
                    onParseFileError(e.target.error, files[0].name);
                };
                fileReader.onload = function (e) {
                    $("#loadingLabel").text("Succeed to load file");
                    $("#loadingLabel").hide(3000);
                    //console.log("loaded json file", e);
                    var dataResult = JSON.parse(e.target.result);
                    if (dataResult && dataResult.length > 0) {
                        var columns = [];
                        $.each(dataResult[0], function (name) {
                            columns.push({ data: name, title: name });
                        });
                        onFileDataLoaded(dataResult, columns);
                    }
                };
                if (files[0].name.startsWith("http") > 0) {
                    fileReader.readAsDataURL(files[0]);
                } else {
                    fileReader.readAsText(files[0], $('#encoding').val());
                }
            } else {
                $('#dataFile').parse({
                    config:
                    {
                        delimiter: $('#delimiter').val(),
                        header: $('#header').prop('checked'),
                        dynamicTyping: $('#dynamicTyping').prop('checked'),
                        skipEmptyLines: true,
                        preview: 0,
                        step: null,
                        encoding: $('#encoding').val(),
                        worker: false, //should be false, or it has problem in IE
                        comments: '',
                        complete: onParseFileComplete,
                        error: onParseFileError,
                        download: false
                    }
                });
            }

            $("#loadingLabel").text("Loading file...");
            $("#loadingLabel").show();
        }
    }

    //send notification in modal dialog
    function notifyMessage(title, message) {
        $("#myModal .modal-title").text(title);
        $("#myModal .modal-body").text(message);
        $("#myModal").modal('show');
    }

    function groupBy(array, key, groupExpression) {
        var groupFunction = null;
        if (groupExpression && groupExpression !== '' && groupExpression.trim() !== '') {
            groupFunction = new Function('dataItem', 'return ' + groupExpression);
        }
        return array.reduce(function (objectsByKeyValue, dataItem) {
            var value;
            if (groupFunction) {
                value = groupFunction(dataItem);
            } else if (key && key !== '') {
                value = dataItem[key];
            } else {
                throw new Error("should either specify groupExpression or key.");
            }

            objectsByKeyValue[value] = (objectsByKeyValue[value] || []).concat(dataItem);

            return objectsByKeyValue;
        }, {});
    }

    function getAggregateValue(array, key, aggregateOption) {
        if (!array || array.length <= 0) {
            return null;
        }
        if (!aggregateOption || aggregateOption === "") {
            return array[0][key];
        } else if (aggregateOption === "Sum") {
            return array.reduce(function (total, currentObj) {
                return total += parseFloat(currentObj[key]);
            }, 0);
        } else if (aggregateOption === "Count") {
            var countObj =
                array.reduce(function (countObj, currentObj) {
                    if (!countObj.hasOwnProperty(currentObj[key])) {
                        countObj[currentObj[key]] = true;
                        countObj.count += 1;
                    }
                    return countObj;
                }, { count: 0 });
            return countObj.count;
        } else if (aggregateOption === "Avg") {
            return array.reduce(function (total, currentObj) {
                return total += parseFloat(currentObj[key]);
            }, 0) / array.length;
        } else if (aggregateOption === "Max") {
            return array.reduce(function (max, currentObj) {
                return max < currentObj[key] ? currentObj[key] : max;
            }, array[0][key]);
        } else if (aggregateOption === "Min") {
            return array.reduce(function (min, currentObj) {
                return min > currentObj[key] ? currentObj[key] : min;
            }, array[0][key]);
        } else if (aggregateOption === "First") {
            return array[0][key];
        } else if (aggregateOption === "Last") {
            return array[array.length - 1][key];
        } else {
            throw new Error("Unsupport aggregate option " + aggregateOption);
        }
    }

    function generateSerieName(serieOption) {
        if (serieOption.aggregateOption && serieOption.aggregateOption !== "") {
            return serieOption.aggregateOption + "(" + serieOption.field + ")";
        }
        return serieOption.field;
    }

    function generateChart(event) {
        //console.log("generate clicked", event);
        if (!event.target) {
            console.error("No target in event.", event);
            return;
        }
        //only genereate chart for current panel
        var chartContainer = $(event.target).parents(".my-chart-container");
        var chartType = chartContainer.find(".my-chart-type").val();
        var xAxis = chartContainer.find(".my-x-axis").val();
        var xAxisType = chartContainer.find(".my-x-axis-type").val();
        var filterExpression = chartContainer.find(".my-filter-expression").val().trim();
        var groupExpression = chartContainer.find(".my-group-expression").val().trim();
        var selectedYSeriesOptionElements = $(".my-selected-y-serie", chartContainer);
        var ySeriesOptions = [];
        for (var i = 0; i < selectedYSeriesOptionElements.length; ++i) {
            if (!$(selectedYSeriesOptionElements[i]).val() || $(selectedYSeriesOptionElements[i]).val() === "") {
                continue;
            }
            var options = {
                field: $(selectedYSeriesOptionElements[i]).val(),
                yAxisPostion: $(selectedYSeriesOptionElements[i]).attr("data-y-axis-position"),
                aggregateOption: $(selectedYSeriesOptionElements[i]).attr("data-y-axis-aggregate-type")
            };
            //check whether there is duplicate serie
            var isDuplicateSerie = false;
            for(var j = 0; j < ySeriesOptions.length; ++j){
                if (ySeriesOptions[j].field === options.field 
                    && ySeriesOptions[j].aggregateOption === options.aggregateOption) {
                        isDuplicateSerie = true;
                    }
            }
            if (!isDuplicateSerie) {
                ySeriesOptions.push(options);
            }
        }
        // console.log("selected type: ", chartType);
        // console.log("selected x axis: ", xAxis, xAxisType);
        // console.log("selected y series: ", ySeries);
        // console.log("filterExpression: ", filterExpression);
        // console.log("groupExpression: ", groupExpression);
        // console.log("aggregate options: ", aggregateOptionsObj);

        if (!xAxis) {
            notifyMessage("Warning", "Please select an xAxis!");
            return;
        }

        if (!ySeriesOptions || ySeriesOptions.length < 1) {
            notifyMessage("Warning", "Please select at least one series!");
            return;
        }

        var seriesData = [];
        var dimensions = [];
        dimensions.push(xAxis);
        $.each(ySeriesOptions, function (_, ySerie) {
            dimensions.push(generateSerieName(ySerie));
        });
        //apply filter expression
        var filteredDataResults = [];
        if (filterExpression && filterExpression !== '' && filterExpression.trim() !== '') {
            var filterFunction = new Function('dataItem', 'return ' + filterExpression);
            $.each(dataResults, function (_, dataItem) {
                if (!filterFunction(dataItem)) {
                    return;
                }
                filteredDataResults.push(dataItem);
            });
        } else {
            filteredDataResults = dataResults;
        }

        //apply group expression
        var groupedDataObj = groupBy(filteredDataResults, xAxis, groupExpression);
        $.each(groupedDataObj, function (key, valueArray) {
            var dataArray = [];
            dataArray.push(key);
            $.each(ySeriesOptions, function (_, ySerie) {
                dataArray.push(getAggregateValue(valueArray, ySerie.field, ySerie.aggregateOption));
            });
            seriesData.push(dataArray);
        });
        
        //sort data by xAxis
        seriesData = 
                    seriesData.sort(function(a, b) {
                        if (a[0] < b[0]) {
                            return -1;
                        } else if (a[0] === b[0]) {
                            return 0;
                        } else {
                            return 1;
                        }
                    });

        //console.log("final chart data: ", xAxisData, seriesData);

        var chartOptions = {
            title: {
                text: $(".my-chart-title", chartContainer).val(),
                left: 'center',
                top: 10
            },
            tooltip: {
                trigger: 'axis',
                axisPointer: {
                    show: true,
                    type: 'cross',
                    lineStyle: {
                        type: 'dashed',
                        width: 1
                    }
                }
            },
            toolbox: {
                show: true,
                feature: {
                    mark: { show: true },
                    dataView: { show: true, readOnly: false },
                    restore: { show: true },
                    saveAsImage: { show: true },
                    magicType: {
                        show: true,
                        title: {
                            line: '折线图切换',
                            bar: '柱形图切换',
                            scatter: '散点切换',
                            pie: '饼图切换'
                        },
                        type: ['line', 'bar', 'scatter', 'pie']
                    }
                }
            },
            dataZoom: [
                {
                    type: 'slider',
                    show: chartType !== 'pie' ? true : false
                },
                {
                    type: 'inside',
                    disabled: chartType !== 'pie' ? false : true
                }
            ],
            legend: {
                show: chartType === 'pie' && seriesData.length > 8 ? false : true,
                top: 30
            },
            dataset: {
                source: seriesData,
                sourceHeader: false,
                dimensions: dimensions
            },
            xAxis: [
                {
                    type: xAxisType,
                    //splitNumber: 10,
                    show: chartType !== 'pie' ? true : false
                }
            ],
            yAxis: [
            ],
            series: []
        };
        //handle yAxis, we may have one or two y axis
        var leftAxisIndex = -1, rightAxisIndex = -1, axisIndex = -1;
        $.each(ySeriesOptions, function (_, serieOption) {
            if (!serieOption.yAxisPostion || serieOption.yAxisPostion === 'left') {
                if (leftAxisIndex < 0) {
                    leftAxisIndex = ++axisIndex;
                    serieOption.yAxisIndex = leftAxisIndex;
                    chartOptions.yAxis.push({
                        type: 'value',
                        show: chartType !== 'pie' ? true : false,
                        position: 'left'
                    });
                    chartOptions.dataZoom.push({
                        type: 'slider',
                        show: chartType !== 'pie' ? true : false,
                        yAxisIndex: leftAxisIndex,
                        filterMode: 'empty',
                        showDataShadow: false,
                        left: '4%'
                    });
                }
                serieOption.yAxisIndex = leftAxisIndex;
            } else {
                if (rightAxisIndex < 0) {
                    rightAxisIndex = ++axisIndex;
                    chartOptions.yAxis.push({
                        type: 'value',
                        show: chartType !== 'pie' ? true : false,
                        position: 'right'
                    });
                    chartOptions.dataZoom.push({
                        type: 'slider',
                        show: chartType !== 'pie' ? true : false,
                        yAxisIndex: rightAxisIndex,
                        filterMode: 'empty',
                        showDataShadow: false,
                        left: '95%'
                    });
                }
                serieOption.yAxisIndex = rightAxisIndex;
            }
        });
        if (chartType === 'pie') {
            var pieOptions = {
                tooltip: {
                    trigger: 'item',
                    formatter: "{a} <br/>{b} : {c} ({d}%)"
                },
                label: {
                    normal: {
                        textStyle: {
                            color: 'rgba(255, 255, 255, 0.3)'
                        }
                    }
                },
                labelLine: {
                    normal: {
                        lineStyle: {
                            color: 'rgba(255, 255, 255, 0.3)'
                        },
                        smooth: 0.2,
                        length: 10,
                        length2: 20
                    }
                }
            };
            chartOptions = $.extend({}, chartOptions, pieOptions);
        }
        $.each(ySeriesOptions, function (_, ySerie) {
            var serieName = generateSerieName(ySerie);            
            var series = {
                name: serieName,
                type: chartType,
                yAxisIndex: ySerie.yAxisIndex
            };
            if (chartType !== 'pie') {
                series.encode = {
                    x: xAxis,
                    y:serieName,
                    itemName: xAxis
                };
            }
            else {
                series.encode = {
                    tooltip: serieName,
                    value: serieName,
                    itemName: xAxis
                };
            }
            chartOptions.series.push(series);
        });
        var chartElement = chartContainer.find(".my-chart")[0];
        var chart = echarts.getInstanceByDom(chartElement);
        if (!chart) {
            chart = echarts.init(chartElement);
        } else {
            chart.clear();
        }
        chart.setOption(chartOptions);
    }

    function removeCurrentChart(event) {
        //console.log("remove chart clicked", event);
        if (!event.target) {
            console.error("No target in event.", event);
            return;
        }
        //only genereate chart for current panel
        var chartContainer = $(event.target).parents(".my-chart-container");
        chartContainer.remove();
    }

    function addNewChart() {
        var chartTemplate = $("#chart-template").html();
        chartTemplate = chartTemplate.replace(/\$\{chartNum\}/gm, ++chartIndex);
        $("#chartsPanel").append(chartTemplate);
        updateChartsOptions($("#chart" + chartIndex));
    }

    function updateDataTagToSelectedYSerie(event, sourceElementSelector, dataAttribute) {
        if (!event.target) {
            console.error("No target in event.", event);
            return;
        }

        var rowContainer = $(event.target).parents("tr");
        var sourceElement = $(sourceElementSelector, rowContainer);
        var ySelectedYSerie = $(".my-selected-y-serie", rowContainer);
        ySelectedYSerie.attr(dataAttribute, sourceElement.val());
    }

    function yAxisPositionChanged(event) {
        updateDataTagToSelectedYSerie(event, ".my-y-axis-position", "data-y-axis-position");
    }

    function yAxisAggreateTypeChanged(event) {
        updateDataTagToSelectedYSerie(event, ".my-y-axis-aggregate-type", "data-y-axis-aggregate-type");
    }

    function removeCurrentSerieRow(event){
        if (!event.target) {
            console.error("No target in event.", event);
            return;
        }
        event.preventDefault();

        var currentRow = $(event.target).parents("tr");
        currentRow.remove();
    }

    function addNewSerieRow(event) {
        if (!event.target) {
            console.error("No target in event.", event);
            return;
        }
        event.preventDefault();
        
        var seriesTable = $("tbody", $(event.target).parents(".my-y-series"));
        addNewSerieRowInTbody(seriesTable);
        $("tr:last-child .my-selected-y-serie", seriesTable).append(axisOptions);

    }

    function addNewSerieRowInTbody(tbody) {
        var rowTemplate = $("#serie-row-template").html();
        tbody.append(rowTemplate);
    }

    $(document).ready(function () {
        addNewChart();
        $('#dataFile').on('change', handleFileSelect);
        $("#addChartBtn").on("click", addNewChart);
        //$("#chartsPanel").on('input propertychange', ".my-group-expression", groupExpressionChanged);
        $("#chartsPanel").on('click', ".my-generate-btn", generateChart);
        $("#chartsPanel").on('click', ".my-remove-chart-btn", removeCurrentChart);
        $("#chartsPanel").on('change', ".my-y-axis-position", yAxisPositionChanged);
        $("#chartsPanel").on('change', ".my-y-axis-aggregate-type", yAxisAggreateTypeChanged);        
        $("#chartsPanel").on('click', ".remove-y-serie-button", removeCurrentSerieRow);
        $("#chartsPanel").on('click', ".add-y-serie-button", addNewSerieRow);

        window.addEventListener("resize", function () {
            //resize all charts
            var chartElements = document.getElementsByClassName("my-chart");
            $.each(chartElements, function (_, element) {
                var chart = echarts.getInstanceByDom(element);
                if (chart) {
                    chart.resize();
                }
            });
        });
    });
})();